package cn.lg.soar.flow.biz.client;

import cn.lg.soar.common.util.AssertUtil;
import cn.lg.soar.common.util.DatetimeUtil;
import cn.lg.soar.common.util.ParameterUtil;
import cn.lg.soar.common.util.current.UserContext;
import cn.lg.soar.common.util.data.DataUtil;
import cn.lg.soar.core.annotations.ApiClient;
import cn.lg.soar.flow.api.constant.ModuleConst;
import cn.lg.soar.flow.api.enums.AuditResultEnum;
import cn.lg.soar.flow.api.enums.AuditTypeEnum;
import cn.lg.soar.flow.api.model.*;
import cn.lg.soar.flow.api.model.node.FieldInfo;
import cn.lg.soar.flow.api.model.node.Node;
import cn.lg.soar.flow.api.model.node.UserNode;
import cn.lg.soar.flow.api.service.IFlowTaskApi;
import cn.lg.soar.flow.api.utils.ProcessUtils;
import cn.lg.soar.flow.biz.entity.SoarApprover;
import cn.lg.soar.flow.biz.entity.SoarAuditLog;
import cn.lg.soar.flow.biz.entity.SoarProcessVersion;
import cn.lg.soar.flow.biz.model.PassTaskDTO;
import cn.lg.soar.flow.biz.model.RejectTaskDTO;
import cn.lg.soar.flow.biz.service.FlowInstanceService;
import cn.lg.soar.flow.biz.service.SoarApproverService;
import cn.lg.soar.flow.biz.service.SoarAuditLogService;
import cn.lg.soar.flow.biz.service.SoarProcessVersionService;
import cn.lg.soar.system.api.service.IUserApi;
import io.swagger.v3.oas.annotations.Hidden;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.*;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author luguoxiang
 * @date 2022/1/3
 * 开源项目：https://gitee.com/lgx1992/lg-soar 求star！请给我star！请帮我点个star！
 */
@Slf4j
@Hidden
@ApiClient("boot")
@RestController
@RequestMapping(ModuleConst.CLIENT_PATH + "/flow-task")
public class FlowTasApiClient implements IFlowTaskApi {

    @Autowired
    private SoarProcessVersionService soarProcessService;
    @Autowired
    private FlowInstanceService flowInstanceService;
    @Autowired
    private SoarAuditLogService auditLogService;
    @Autowired
    private SoarApproverService soarApproverService;
    @Autowired
    private IUserApi userApi;


    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private IdentityService identityService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private HistoryService historyService;

    @Transactional(rollbackFor = Exception.class)
    @PostMapping("startFlow")
    @Override
    public FlowInfoDTO startFlow(@RequestBody StartFlowDTO startFlowDTO) {
        Long initiator = startFlowDTO.getInitiator();
        String flowCode = startFlowDTO.getFlowCode();
        SoarProcessVersion soarProcess = soarProcessService.getByCode(flowCode);

        // 1. 查询最新版本的流程定义
        ProcessDefinition processDefinition = repositoryService
                .createProcessDefinitionQuery()
                .processDefinitionKey(flowCode)
                .latestVersion()
                .singleResult();
        if (processDefinition == null) {
            throw new RuntimeException("流程定义不存在，key: " + flowCode);
        }
        // 流程标题参数
        Map<String, Object> params = new  HashMap<>();
        params.put("发起时间", DatetimeUtil.format(LocalDateTime.now()));
        params.put("流程名称", soarProcess.getName());

        // 2. 启动流程实例
        String bizId = startFlowDTO.getBizId();
        Long bizOrgId = startFlowDTO.getBizOrgId();
        Map<String, Object> variables = startFlowDTO.getVariables() != null ? startFlowDTO.getVariables() : new HashMap<>();
        // 发起人
        if (initiator != null) {
            identityService.setAuthenticatedUserId(initiator.toString());
            variables.put("initiator", ProcessUtils.toUserApproval(initiator));
            params.put("发起人", userApi.getById(initiator).getNickname());
        }
        if (bizOrgId != null) {
            variables.put("bizOrgId", bizOrgId);
        }
        Map<String, String> fieldMap = soarProcess.getFields()
                .stream()
                .collect(Collectors.toMap(FieldInfo::getKey, FieldInfo::getName));
        variables.forEach((k, v) -> {
            String kn = fieldMap.get(k);
            if (kn != null) {
                params.put("表单·" + fieldMap.get(k), v);
            }
        });
        String titleTemplate = ProcessUtils.fillTemplate(soarProcess.getTitleTemplate(), params) ;

        // 启动流程
        ProcessInstance processInstance = runtimeService
                .createProcessInstanceBuilder()
                .processDefinitionKey(flowCode)
                .businessKey(bizId)
                .variables(variables)
                .name(titleTemplate)
                .start();

        String procInstId = processInstance.getId();
        // 自选审批人
        Map<String, Collection<String>> approvers = startFlowDTO.getApprovers();
        setApprovers(procInstId, approvers);
        // 自动跳过第一个节点任务
        if (!Boolean.FALSE.equals(startFlowDTO.getSkipFirstNode())) {
            List<Task> tasks = taskService.createTaskQuery()
                    .processInstanceId(procInstId)
                    .active()
                    .list();
            AssertUtil.notEmpty(tasks, "未能找到任务");
            AssertUtil.isTrue(tasks.size() == 1, "无法跳过多个任务");
            // 自动完成所有当前任务（通常只有一个）
            List<ApprovalTaskDTO> list = tasks.stream().map(task -> {
                ApprovalTaskDTO taskDTO = new ApprovalTaskDTO();
                taskDTO.setTaskId(task.getId());
                taskDTO.setTaskKey(task.getTaskDefinitionKey());
                taskDTO.setProcessId(task.getProcessDefinitionId());
                return taskDTO;
            }).toList();
            String comments = startFlowDTO.getComments();
            PassTaskDTO passTaskDTO = new PassTaskDTO();
            passTaskDTO.setVariables(variables);
            passTaskDTO.setComments(comments == null ? "自动通过" : comments);
            passTaskDTO.setTasks(list);
            passTaskDTO.setUserId(UserContext.getUserId());
            flowInstanceService.passTask(passTaskDTO);
        }

        // 3. 封装返回结果
        HistoricProcessInstance historicProcessInstance = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(procInstId)
                .singleResult();
        FlowInfoDTO result = new FlowInfoDTO();
        result.setProcInstId(procInstId);
        result.setStartTime(historicProcessInstance.getStartTime());
        result.setEndTime(historicProcessInstance.getEndTime());
        result.setFlowCode(processDefinition.getKey());
        result.setFlowId(processDefinition.getId());

        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @PostMapping("completeTask")
    @Override
    public FlowInfoDTO completeTask(@RequestBody CompleteTaskDTO completeTaskDTO) {
        String comments = completeTaskDTO.getComments();
        ParameterUtil.notEmpty(comments, "请输入审批意见");
        Long userId = UserContext.getUserId();
        String taskId = completeTaskDTO.getTaskId();
        String procInstId = completeTaskDTO.getProcInstId();
        List<ApprovalTaskDTO> tasks;
        if (DataUtil.isValuable(taskId)) {
            Task task = taskService.createTaskQuery()
                    .taskId(taskId)
                    .active() // 只查激活中的任务
                    .singleResult();
            AssertUtil.notNull(task, "任务不存在");
            procInstId = task.getProcessInstanceId();
            ApprovalTaskDTO taskDTO = new ApprovalTaskDTO();
            taskDTO.setTaskId(task.getId());
            taskDTO.setTaskKey(task.getTaskDefinitionKey());
            taskDTO.setProcessId(task.getProcessDefinitionId());
            tasks = List.of(taskDTO);
        } else if (DataUtil.isValuable(procInstId)) {
            tasks = flowInstanceService.approvalTasks(userId, procInstId, completeTaskDTO.getTaskKey());
            ParameterUtil.notEmpty(tasks, "没有您的审批任务");
        } else {
            throw new RuntimeException("procInstId 和 taskId 必须有一个");
        }
        Map<String, Object> variables = completeTaskDTO.getVariables() != null ? new HashMap<>(completeTaskDTO.getVariables()) : new HashMap<>();
        // === 3. 完成任务（变量会传递到后续节点）
        identityService.setAuthenticatedUserId(ProcessUtils.toUserApproval(userId));
        // 自选审批人
        Map<String, Collection<String>> approvers = completeTaskDTO.getApprovers();
        setApprovers(procInstId, approvers);
        // 完成任务
        if (AuditResultEnum.pass.equals(completeTaskDTO.getResult())) {
            PassTaskDTO passTaskDTO = new PassTaskDTO();
            passTaskDTO.setVariables(variables);
            passTaskDTO.setComments(comments);
            passTaskDTO.setTasks(tasks);
            passTaskDTO.setUserId(userId);
            flowInstanceService.passTask(passTaskDTO);
        } else {
            RejectTaskDTO rejectTaskDTO = new RejectTaskDTO();
            rejectTaskDTO.setVariables(variables);
            rejectTaskDTO.setComments(comments);
            rejectTaskDTO.setTasks(tasks);
            rejectTaskDTO.setUserId(userId);
            rejectTaskDTO.setProcInstId(procInstId);
            rejectTaskDTO.setBackNodeKey(completeTaskDTO.getBackNodeKey());
            flowInstanceService.rejectTask(rejectTaskDTO);
        }

        // === 4. 查询流程实例最新状态
        HistoricProcessInstance processInstance = historyService
                .createHistoricProcessInstanceQuery()
                .processInstanceId(procInstId)
                .singleResult();

        // === 5. 构造返回结果
        FlowInfoDTO result = new FlowInfoDTO();
        result.setProcInstId(procInstId);
        result.setStartTime(processInstance.getStartTime());
        result.setEndTime(processInstance.getEndTime());
        result.setFlowCode(processInstance.getProcessDefinitionKey());
        result.setFlowId(processInstance.getProcessDefinitionId());

        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @PostMapping("revoke")
    @Override
    public void revoke(@RequestBody RevokeDTO revokeDTO) {
        Long userId = UserContext.getUserId();
        String reason = revokeDTO.getReason();
        identityService.setAuthenticatedUserId(ProcessUtils.toUserApproval(userId));
        List<Task> list = taskService.createTaskQuery()
                .active()
                .list();
        if (!list.isEmpty()) {
            // 添加审批日志
            List<SoarAuditLog> auditLogs = list.stream().map(task -> {
                SoarAuditLog auditLog = new SoarAuditLog();
                auditLog.setTaskId(task.getId());
                auditLog.setUserId(userId);
                auditLog.setResult(AuditTypeEnum.revoke);
                auditLog.setComments(reason);
                return auditLog;
            }).toList();
            auditLogService.saveBatch(auditLogs);
        }
        runtimeService.deleteProcessInstance(revokeDTO.getProcInstId(), reason);
    }

    @GetMapping("approvalTasks")
    @Override
    public List<ApprovalTaskDTO> approvalTasks(@RequestParam("userId") Long userId, @RequestParam("procInstId") String procInstId) {
        return flowInstanceService.approvalTasks(userId, procInstId);
    }

    @GetMapping("currentProcInstIds")
    @Override
    public List<String> currentProcInstIds(@RequestParam("userId") Long userId, @RequestParam("flowCode") String flowCode) {
        return List.of();
    }

    @GetMapping("currentTaskUserIds")
    @Override
    public List<TaskInfoDTO> currentTaskUserIds(@RequestParam("procInstIds") Collection<String> procInstIds) {
        return List.of();
    }

    @GetMapping("currentTasks")
    @Override
    public List<TaskInfoDTO> currentTasks(@RequestParam("procInstIds") Collection<String> procInstIds) {
        return List.of();

    }

    @GetMapping("auditLogList")
    @Override
    public List<AuditLogDTO> auditLogList(@RequestParam("procInstId") String procInstId) {
        return auditLogService.list(procInstId);
    }

    @GetMapping("auditLogList2")
    @Override
    public List<AuditLogDTO> auditLogList(@RequestParam("flowCode") String flowCode, @RequestParam("bizId") String bizId) {
        return auditLogService.list(flowCode, bizId);
    }

    @GetMapping("progress")
    @Override
    public ProgressVO progress(@RequestParam("procInstId") String procInstId) {
        return flowInstanceService.progress(procInstId);
    }

    @GetMapping("taskDetail")
    @Override
    public TaskDetailDTO taskDetail(@RequestParam("taskId") String taskId) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        ProgressVO progress = flowInstanceService.progress(task.getProcessInstanceId());
        List<AuditLogDTO> logs = progress.getLogs();
        List<Node> nodes = progress.getNodes();
        TaskDetailDTO dto = new TaskDetailDTO();
        dto.setTaskId(taskId);
        dto.setTaskKey(task.getTaskDefinitionKey());
        dto.setTaskName(task.getName());
        dto.setLogs(logs);
        dto.setNodes(nodes);
        for (AuditLogDTO x : logs) {
            if (x.getTaskId().equals(taskId)) {
                dto.setApprovers(x.getApprovers());
            }
        }
        List<UserNode> userNodes = ProcessUtils.getUserNodes(nodes);
        String taskKey = task.getTaskDefinitionKey();
        for (UserNode node : userNodes) {
            if (node.getKey().equals(taskKey)) {
                dto.setButtonPermits(node.getButtonPermits());
                dto.setFieldPermits(node.getFieldPermits());
            }
        }
        return dto;
    }

    private void setApprovers(String procInstId, Map<String, Collection<String>> approvers) {
        // 设置业务指定审批人
        if (DataUtil.isValuable(approvers)) {
            List<SoarApprover> soarApprovers = new ArrayList<>(approvers.size());
            approvers.forEach((nodeKey, list) -> {
                if (DataUtil.isValuable(list)) {
                    SoarApprover soarApprover = new SoarApprover();
                    soarApprover.setProcInstId(procInstId);
                    soarApprover.setNodeKey(nodeKey);
                    soarApprover.setApprovers(new HashSet<>(list));
                    soarApprovers.add(soarApprover);
                }
            });
            if (DataUtil.isValuable(soarApprovers)) {
                soarApproverService.saveBatch(soarApprovers);
            }
        }
    }
}
